--- 模块功能：接收单片机数据，并将数据发送给服务器
-- @author 毛金龙
-- @release 2018.11.19

module(...,package.seeall)

require"utils"
require"pm"
require "http"
require "tcp"
require "socketOutMsg"

--[[
功能定义：
uart按照帧结构接收外围设备的输入，收到正确的指令后，回复ASCII字符串

帧结构如下：
无需刻意定义帧，根据单片机传送过来的数据进行分析，单片机发送数据类型为type:content(type:error/gps/等)，content:具体数据
明天再约定具体各数据格式

]]


--串口ID,1对应uart1
--如果要修改为uart2，把UART_ID赋值为2即可
local UART_ID = 2
--帧头类型以及帧尾
local CMD_SCANNER,CMD_GPIO,CMD_PORT,FRM_TAIL = 1,2,3,string.char(0xC0)
--串口读到的数据缓冲区
local rdbuf = ""


--[[
函数名：parse
功能  ：按照帧结构解析处理一条完整的帧数据
参数  ：
        data：所有未处理的数据
返回值：第一个返回值是一条完整帧报文的处理结果，第二个返回值是未处理的数据
]]
local function parse(data)
    if not data then return end    
    
    local tail = string.find(data,string.char(0xC0))
    if not tail then return false,data end    
    local cmdtyp = string.byte(data,1)
    local body,result = string.sub(data,2,tail-1)
    
    log.info("testUart.parse",data:toHex(),cmdtyp,body:toHex())
    
    if cmdtyp == CMD_SCANNER then
        write("CMD_SCANNER")
    elseif cmdtyp == CMD_GPIO then
        write("CMD_GPIO")
    elseif cmdtyp == CMD_PORT then
        write("CMD_PORT")
    else
        write("CMD_ERROR")
    end
    
    return true,string.sub(data,tail+1,-1)    
end
--[[
-- 格式化时间为十六进制数据
-- ]]
local function handleTimeStamp()
    local tm = misc.getClock()
    local timeStamp =string.format("%02d%02d%02d%02d%02d%02d", tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec)
    local timeHex = string.char(tm.year-2000)..string.char(tm.month)..string.char(tm.day)..string.char(tm.hour)..string.char(tm.min)..string.char(tm.sec)
    log.info("stringToHex",string.toHex(timeHex," "))
    return timeHex;
end
function handleGPSAndVoltage(gps)
    gps = string.sub(gps,5,#gps-1)
    local location = string.split(gps,",");
    local returnString ;
    local latitude = location[1];
    local longtitude = location[2];
    local voltage = location[3];
    log.info("test---------------->",#longtitude,longtitude)
    returnString = returnString and returnString..longtitude or longtitude
    returnString = returnString and returnString..latitude or latitude
    local voltageNum = tonumber(voltage)*10;
--    log.info("voltage_num-------------->",voltageNum);
--    local voltageString = string.char(voltageNum);
--    log.info("voltage_string-------------->",voltageString);
    if(voltageNum<=255)then
        voltageNum = string.char(0,voltageNum)
    else
        local highNum = bit.band(bit.rshift(voltageNum,8),0xff)
        local lowNum = bit.band(voltageNum,0xff)
        voltageNum = string.char(highNum,lowNum)
    end
    log.info("voltage_hex-------------->",string.toHex(voltageNum," "));
    returnString = returnString and returnString..voltageNum or voltageNum
    log.info("latitude",latitude);
    log.info("longtitude",longtitude);
    log.info("returnString",string.toHex(returnString," "))
    return returnString;
end
--处理gps及运沙船编号
function handleGPSAndNum(gps,inpairsnum)
    log.info("gps",gps);
    log.info("inparsnum",inpairsnum);
    local table_aliyun = {}
    if(gps~="" and gps~=nil)then
        local gps = string.sub(gps,5,#gps-1)
        local location = string.split(gps,",");
        local latitude = location[1];
        table_aliyun["latitude"]= latitude;
        local longtitude = location[2];
        table_aliyun["longtitude"]= longtitude;
        local voltage = location[3];
        table_aliyun["voltage"]= voltage;
    end
    if(inpairsnum~="" and inpairsnum~=nil)then
        log.info("inpairsnum",inpairsnum)
        table_aliyun["inpairsnum"]= inpairsnum;
    end
--    table转json字符串
    --      将table转为json串
    local jsonContent = json.encode(table_aliyun)
    log.info("json_playload",jsonContent)
    return jsonContent;
end
--[[
函数名：proc
功能  ：处理从串口读到的数据
参数  ：
        data：当前一次从串口读到的数据
返回值：无
]]
local function proc(data)
    if not data or string.len(data) == 0 then return end
    --追加到缓冲区
    rdbuf = rdbuf..data    
    
    local result,unproc
    unproc = rdbuf
    --根据帧结构循环解析未处理过的数据
    while true do
        result,unproc = parse(unproc)
        if not unproc or unproc == "" or not result then
            break
        end
    end

    rdbuf = unproc or ""
end
local function sndUART(result)
    log.info("socketOutMsg.sndHeartCb",result)
end
local function handleDeviceNum(stringNum)
    local mytable= {};
    for i=1,4,1 do
        table.insert(mytable,i, tonumber(string.sub(stringNum,i,i)))

    end

    return mytable;
end
--[[
函数名：read
功能  ：读取串口接收到的数据
参数  ：无
返回值：无
]]
local function read()
    local data = ""
    --底层core中，串口收到数据时：
    --如果接收缓冲区为空，则会以中断方式通知Lua脚本收到了新数据；
    --如果接收缓冲器不为空，则不会通知Lua脚本
    --所以Lua脚本中收到中断读串口数据时，每次都要把接收缓冲区中的数据全部读出，这样才能保证底层core中的新数据中断上来，此read函数中的while语句中就保证了这一点
    while true do        
        data = uart.read(UART_ID,"*l")
        if not data or string.len(data) == 0 then break end
        --打开下面的打印会耗时
        log.info("testUart.read bin",data)
--        http.request("GET","http://118.24.33.134:3003/?data="..string.urlEncode(data),nil,nil,nil,30000,cbFnc)
--        log.info("testUart.read hex",data:toHex())
        --截取数据并进行分析，如果符合上传条件回复“OK”并进行上传
        local aResult = string.split(data,":");
        if(aResult)then
            local type = aResult[1];
            local receiveData = aResult[2];
            if(type=="gps" or type == "error" or type == "adc" or type=="devicenum"  or type=="wireless")then
                --回复OK
                if(type == "gps")then
                    --将经纬度拆分
--                    local location = string.split(aResult[2],",");
                    log.info("position",data)
--                    if(tcp.isReady) then
--                        socketOutMsg.insertMsg(socketOutMsg.composeHeartData(PROTOORDER_HEART,{0x00}),{cb=sndUART})
--                    end
                    log.info("ERRCODE",_G.ERRCODE)
                    if(_G.ERRCODE == ERR_NO)then
                        local timeHexString =  handleTimeStamp();
                        local gpsString = handleGPSAndVoltage(data)
                        socketOutMsg.insertMsg(socketOutMsg.composeHeartData(PROTOORDER_GPS,timeHexString..gpsString),{})
                    end
                    local jsonstring = handleGPSAndNum(data)
--                    myALiYun.publishTest(jsonstring,"/update")
                end
                if(type == "devicenum")then
                    log.info("mydevicenum",data);
--                    将字符串设备编号处理为table形式
                    local devicenum = handleDeviceNum(receiveData);
--                    for i,v in ipairs(devicenum) do
--                        log.info("device",v);
--                    end
                    _G.DEVICEID = devicenum;
                    _G.DEVICESTRING = string.sub(receiveData,1,4);
                    sys.publish("DEVICEOK")
                end
                if(type=="wireless")then
--                    接收到运沙船发来的设备编号，则直接进行上传
                    log.info("ERRCODE",ERRCODE)
                    if(_G.ERRCODE == ERR_NO)then
                        local timeHexString =  handleTimeStamp();
                        log.info("devicenum",timeHexString..receiveData)
                        socketOutMsg.insertMsg(socketOutMsg.composeHeartData(PROTOORDER_DEVICENUM,timeHexString..string.sub(receiveData,1,4)),{})
                    end
                    local jsonstring =handleGPSAndNum(nil,string.sub(receiveData,1,4))
--                    myALiYun.publishTest(jsonstring,"/devicenum")
                end

--                if(type == "adc")then
--                    log.info("adc",data);
--
--                end
                --上传至服务器
            else
                log.info("数据格式不符");
            end
        else
            --故障记录
            log.info("数据解析错误！");
        end

    end
end

--[[
函数名：write
功能  ：通过串口发送数据
参数  ：
        s：要发送的数据
返回值：无
]]
function write(s)
    log.info("testUart.write",s)
    uart.write(UART_ID,s.."\r\n")
end

local function writeOk()
    log.info("testUart.writeOk")
end


--保持系统处于唤醒状态，此处只是为了测试需要，所以此模块没有地方调用pm.sleep("testUart")休眠，不会进入低功耗休眠状态
--在开发“要求功耗低”的项目时，一定要想办法保证pm.wake("testUart")后，在不需要串口时调用pm.sleep("testUart")
pm.wake("testUart")
--注册串口的数据接收函数，串口收到数据后，会以中断方式，调用read接口读取数据
uart.on(UART_ID,"receive",read)
--注册串口的数据发送通知函数
uart.on(UART_ID,"sent",writeOk)

--配置并且打开串口
uart.setup(UART_ID,9600,8,uart.PAR_NONE,uart.STOP_1)
write(string.char(0x01));
